May 24, 2023
페이먼츠 미션에서 사용한 모달을 라이브러리로 배포하게 되었다. 간단한 모달이지만 이렇게 오픈소스 라이브러리를 배포해보는 경험은 처음이라 글로 남겨보기로 정했다. 그리고 한 번도 프로젝트 설정을 직접 해본적이 없어 CRA가 아닌 웹팩을 사용하기로 결정했다.
npm init
npm init
명령어를 통해 npm 라이브러리에 올리기 위한 최소한의 정보들을 입력할 수 있다. (package name, description, git repository 등등) 정보를 입력하게 되면 package.json
파일이 생성된다.
npm install react react-dom typescript @types/react @types/react-dom ts-loader
//tsconfig.json
{
"compilerOptions": {
"allowSyntheticDefaultImports": true,
"moduleResolution": "node",
"target": "es6",
"module": "ESNext",
"lib": ["es6", "dom"],
"declaration": true,
"outDir": "dist",
"strict": true,
"jsx": "react-jsx"
},
"include": ["src/index.tsx"]
}
리액트 프로젝트이기 때문에 "jsx" : "react-jsx"
를 설정해줘야 한다.
npm install -D babel-loader @babel/core
npm install -D @babel/preset-env @babel/preset-react @babel/preset-typescript
//.babelrc
{
"presets": [
"@babel/preset-env",
"@babel/preset-react",
"@babel/preset-typescript"
]
}
npm install -D webpack webpack-dev-server webpack-cli
//webpack.config.js
const path = require('path')
const webpack = require('webpack')
const prod = process.env.NODE_ENV === 'production'
module.exports = {
mode: prod ? 'production' : 'development',
devtool: prod ? 'hidden-source-map' : 'eval',
entry: './src/index.tsx',
resolve: {
extensions: ['.js', '.jsx', '.ts', '.tsx'],
},
module: {
rules: [
{
test: /\.tsx?$/,
exclude: /node_modules/,
use: ['babel-loader', 'ts-loader'],
},
{
test: /\.(js|jsx)$/,
exclude: /node_modules/,
use: ['babel-loader'],
},
],
},
output: {
path: path.join(__dirname, '/dist'),
filename: 'index.js',
},
}
지금까지 패키지를 설치할 때 별 생각 없이 받았었는데 라이브러리를 배포하면서 dependency
, peer dependency
, dev dependency
의 차이점을 알게 되었다.
내가 만든 라이브러리는 리액트에서만 사용할 수 있기 때문에 peerDependency로 리액트를 관리했다. 그리고 styled-components를 사용했기 때문에 dependency에 styled-components를 추가해줬다. (사실 굳이 styled-components를 쓸 필요는 없지만 시간이 없기 때문에 익숙한 도구를 사용할 수 밖에 없었다..🥲)
npm info [배포할 패키지명]
npm info
명령어를 통해 자신이 배포할 라이브러리 이름이 중복인지 확인할 수 있다. 404 에러가 뜨게 된다면 존재 하지 않는 이름이기 때문에 사용할 수 있다.
npm login
라이브러리에 배포를 하기 위해서는 npm에 로그인을 해야 한다. npm whoami
명령어를 쓰면 로그인 되어있는지 확인할 수 있다.
package.json을 보면 version을 작성하도록 하고 있다. publish를 할 때마다 꼭 버전을 바꿔줘야 반영이 된다. npm에서 사용하는 버전은 semantic versioning으로 [major].[minor].[patch]
를 규칙으로 한다.
"scripts":{
...
"publish:npm": "rm -rf dist && mkdir dist && tsc"
}
배포하기전 npm publish:npm
이라는 명령어를 통해 빌드한다. (명령어의 이름은 바꿔도 상관 없다.) 매번 dist 아래 내용을 삭제하고 새롭게 빌드를 한다는 뜻이다. 뒤의 tsc
는 타입스크립트를 빌드한다는 뜻이다.
⚠️ 이 전에 babel src -d dist --copy-files
라는 명령어도 사용했었는데 이렇게 하면 추가적인 loader가 필요하다는 에러가 나온다. 정확한 이유는 파악하지 못했지만 copy-files
로 인해 파일이 갱신되지 않는다는 추측을 하고 있다.
npm publish
npm publish
를 통해 npm에 라이브러리를 업로드한다. 이미 업로드 되어있는데 버전만 올려주는 것이라면 npm version patch, minor, major를 사용할 수 있다.
모달을 배포하면서 다양한 라이브러리를 구경해봤다. (chakra-ui, react-modal 등) 문서를 어떻게 작성했는지, 어떤 컴포넌트를 제공해주는지 등을 보고 어떻게 모달을 구성할지 고민해봤다. 하지만 시간이 부족했기에 다음으로 미뤘고 현재는 모달의 위치(정가운데 혹은 아래)만 정해줄 수 있는 상태이다. 고도화를 하게 된다면 chakra-ui처럼 ModalContent, ModalLayer등의 컴포넌트를 제공해서 쉽게 커스터마이징 할 수 있는 모달을 만들고 싶다.
추가로 요즘 재사용성이 높은 컴포넌트를 만드는 것에 대해 고민을 하고 있는데, 모달 말고도 자주 쓰이는 Input이나 Button 같이 자주 쓰는 컴포넌트들을 모아서 나만의 라이브러리를 배포해보고 싶다. 준이 일주일에 한 번씩 버전을 올려보면서 만들어보라고 했는데 뭔가 재미있을 것 같다.
처음이라 막막하기만 했던 라이브러리 배포를 결국엔 성공적으로 끝낼 수 있었다. cra로 했으면 금방 끝낼 수도 있었을 것 같은데 웹팩설정을 하면서 생긴 오류들로 인해 시간이 오래 걸렸다. 하지만 만약 라이브러리를 오래 maintain 할 것이라면 직접 환경 설정을 해보는 것도 좋을 것 같다. 설정들만 하면 라이브러리를 유지하는 것은 어렵지 않다. npm에 배포하는 것은 한 번쯤 해보면 좋은 경험인 것 같다.